#pragma once
#include <lcir/IR/Context.h>
#include <lcir/IR/Dialect.h>
#include <lcir/IR/ExprTemplate.h>
#include <lcir/Utils/Utils.h>

#include <iostream>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>

namespace lcir {
class Expression;
class Value;
class Constant;
class Prototype;
class OpInstance {
  friend class GlobalContext;

 public:
  OpInstance() = default;
  template <class OpTy>
  static OpInstance *get() {
    auto *ctx = GlobalContext::get();
    return ctx->getOpInstance<OpTy>();
  }
};
// 这是一个纯虚类
class Op {
  friend class Expression;
  std::string name{""};
  Dialect *dialect{nullptr};
  OpInstance *instance{nullptr};

 protected:
  Expression *expr{nullptr};
  std::set<ExprTempInstance *> exprTemps;

  Op(std::string_view name, Dialect *dialect, OpInstance *instance)
      : name(name), dialect(dialect), instance(instance) {}
  OpInstance *getInstance() const { return instance; }

 public:
  template <class OtherType>
  bool isa() const {
    return getInstance() == OpInstance::get<OtherType>();
  }
  template <class OtherType>
  OtherType *dyn_cast() {
    if (!isa<OtherType>()) return nullptr;
    return static_cast<OtherType *>(this);
  }
  template <class OtherType>
  const OtherType *dyn_cast() const {
    if (!isa<OtherType>()) return nullptr;
    return static_cast<const OtherType *>(this);
  }

  // 检查操作数的type是否符合该Op约束
  virtual bool checkType(const std::vector<Prototype *> &operandVals) = 0;
  bool checkType(const std::vector<Value *> &operandVals);

  // 推断返回值类型
  std::vector<Prototype *> deduceRetType(const std::vector<Value *> &operandVals);
  virtual std::vector<Prototype *> deduceRetType(const std::vector<Prototype *> &operandProtos) = 0;

  Constant *tryFold(const std::vector<Value *> &operands);
  virtual Constant *fold(const std::vector<Constant *> &) { return nullptr; }

  // 插入链表触发回调函数
  virtual void insertCallBack(Expression *expr) {}

  // 查看是否含有某个ExpreTemp
  template <class ExprTemp>
  bool hasTemp() const {
    return exprTemps.find(ExprTempInstance::get<ExprTemp>()) != exprTemps.end();
  }

  std::string getName() const {
    std::stringstream ss;
    ss << dialect->getName() << "." << name;
    return ss.str();
  }
  Expression *getExpr() const { return expr; }
  std::vector<Operand *> getOperands() const;
  std::vector<Value *> getOperandValues() const;
  std::vector<Value *> getResults() const;
  Value * getResult() const;

 private:
  void setExpr(Expression *exprRef) {
    if (expr)
      throw std::runtime_error("op's expression can't set twice!");
    else
      expr = exprRef;
  }
};

template <class DerviedTy, class DialectTy, class... ExprTemplate>
class OpCRTP : public Op {
  // std::tuple<ExprTemplateTrait<ExprTemplate>...> exprTemps;

 public:
  using opType = OpCRTP<DerviedTy, DialectTy>;
  OpCRTP(std::string_view name) : Op(name, DialectTy::get(), OpInstance::get<DerviedTy>()) {
    using TempT = std::tuple<ExprTemplate...>;
    static_for<sizeof...(ExprTemplate)>(
        [&](auto i) { exprTemps.insert(ExprTempInstance::get<std::tuple_element_t<i, TempT>>()); });
  }
  static OpInstance *getOpInstance() { return OpInstance::get<DerviedTy>(); }

  template <class... Args>
  static std::unique_ptr<DerviedTy> get(Args... args) {
    auto *op = new DerviedTy(args...);
    return std::unique_ptr<DerviedTy>(op);
  }

  template <class ExprTempT>
  bool hasTemp() {
    return true;
  }
  virtual ~OpCRTP() {}
};
}  // namespace lcir